package drds.plus.executor.command_handler.query;

import ch.qos.logback.WithLog;
import drds.plus.executor.ExecuteContext;
import drds.plus.executor.command_handler.CommonCommandHandler;
import drds.plus.executor.cursor.cursor.ISortingCursor;
import drds.plus.executor.cursor.cursor.impl.sort.DistinctCursor;
import drds.plus.executor.repository.Repository;
import drds.plus.executor.utils.Utils;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.ExecutePlan;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.QueryWithIndex;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Filter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Function;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.FunctionType;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.tools.ShouldNeverHappenException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

//@Slf4j
public abstract class AbstractQueryHandler extends CommonCommandHandler implements WithLog {


    public AbstractQueryHandler() {
        super();
    }


    public ISortingCursor handle(ExecuteContext executeContext, ExecutePlan executePlan) throws RuntimeException {
        // 先做查询
        ISortingCursor cursor = doQuery(executeContext, executePlan, null);

        if (executePlan.getSql() == null || (executePlan instanceof QueryWithIndex && ((QueryWithIndex) executePlan).getIndexName() != null)) {
            Query query = (Query) executePlan;

            cursor = processValueFilter(executeContext, query, cursor);

            cursor = processGroupByAndOrderBy(executeContext, query, cursor);

            cursor = processLimitFromTo(executeContext, query, cursor);

            cursor = processColumnAndAlias(executeContext, query, cursor);
        }
        return cursor;
    }

    protected abstract ISortingCursor doQuery(ExecuteContext executeContext, ExecutePlan executePlan, ISortingCursor cursor) throws RuntimeException;

    protected ISortingCursor processValueFilter(final ExecuteContext executeContext, Query query, ISortingCursor cursor) throws RuntimeException {
        Filter valueFilter = query.getValueFilter();
        if (valueFilter != null) {
            cursor = executeContext.getRepository().getCursorFactory().valueFilterCursor(executeContext, cursor, query.getValueFilter());
        }
        return cursor;
    }

    /**
     * <pre>
     * 首先获取四个关键的属性
     *  1. group by ,aggregate_function function 算法
     *  2. distinct
     *  3. order by
     *  4. sort
     * </pre>
     */
    protected ISortingCursor processGroupByAndOrderBy(ExecuteContext executeContext, Query Query, ISortingCursor cursor) throws RuntimeException {
        // 处理 group by 和aggregate function. cursor =
        cursor = processGroupByAndAggregateFunction(executeContext, Query, cursor);

        cursor = processDistinct(executeContext, Query, cursor);

        cursor = processHavingFilter(executeContext, Query, cursor);
        // 接着处理排序
        cursor = processOrderBy(executeContext, cursor, Query, Query.getOrderByList(), true);
        return cursor;
    }

    /**
     * <pre>
     * group by和aggregate FunctionImpl。
     * 对单机来说，原则就是尽可能的使用索引完成count max min的功能。
     * 参考的关键条件有：
     * 1. 是否需要group by
     * 2. 是什么aggregate.
     * 3. 是否需要distinct
     * 4. 是否是merge节点
     * </pre>
     */
    protected ISortingCursor processGroupByAndAggregateFunction(ExecuteContext executeContext, Query query, ISortingCursor cursor) throws RuntimeException {
        // 是否带有group by 列。。
        List<OrderBy> groupByList = query.getGroupByList();
        boolean closeResultCursor = executeContext.isCloseResultCursor();
        final Repository repository = executeContext.getRepository();

        List<Item> itemList = getItemList(query);
        List<Function> aggregateFunctionList = getAggregateFunctionList(itemList);
        // 接着处理group by
        if (groupByList != null && !groupByList.isEmpty()) {

            if (query instanceof MergeQuery && ((MergeQuery) query).isGroupByShardColumns()) {
                // group by sharding column do nothing
            } else {
                // group by之前需要进行排序，按照group by列排序[this is group by]
                cursor = processOrderBy(executeContext, cursor, query, (groupByList), false);
            }
        }

        cursor = executeAggregate(repository, executeContext, query, cursor, closeResultCursor, groupByList, aggregateFunctionList);
        return cursor;
    }

    protected List<Item> getItemList(Query Query) {
        return Query.getItemList() == null ? Collections.EMPTY_LIST : Query.getItemList();
    }

    protected List<Function> getAggregateFunctionList(List<Item> itemList) {
        return getAggregateFunctionListNeedToCalculate(itemList, false);
    }

    /**
     * <pre>
     * 需要计算的函数
     * query节点中
     *  1、scalar函数
     *  2、aggregate函数
     * merge节点中
     *  1、聚合函数
     *  2、子节点中有aggregate函数的scalar函数
     * </pre>
     */
    protected List<Function> getAggregateFunctionListNeedToCalculate(List<Item> itemList, boolean isMergeAggregates) {
        List<Function> aggregateFunctionList = new ArrayList<Function>();
        for (int i = 0; i < itemList.size(); i++) {
            Object object = itemList.get(i);
            if (object instanceof Function) {
                // 如果retColumn中出现了函数名字，那么进入这个逻辑
                Function function = (Function) object;
                if (FunctionType.aggregate_function.equals(function.getFunctionType())) {
                    aggregateFunctionList.add(function);
                } else {
                    List<Function> aggregateFunctionListInThisScalar = new ArrayList<Function>();
                    findAggregateFunctionsInScalar(function, aggregateFunctionListInThisScalar);

                    // 包含聚合函数
                    if (!aggregateFunctionListInThisScalar.isEmpty()) {
                        aggregateFunctionList.add(function);
                        aggregateFunctionList.addAll(aggregateFunctionListInThisScalar);
                    } else {
                        // 不包含聚合函数，merge中就不需要再算一次了
                        if (!isMergeAggregates) {
                            aggregateFunctionList.add(function);
                        }
                    }
                }
            }
        }
        return aggregateFunctionList;
    }

    private void findAggregateFunctionsInScalar(Function function, List<Function> functionList) {
        if (FunctionType.aggregate_function.equals(function.getFunctionType())) {
            functionList.add(function);
        }
        for (Object arg : function.getArgList()) {
            if (arg instanceof Function) {
                this.findAggregateFunctionsInScalar((Function) arg, functionList);
            }
        }

    }

    protected ISortingCursor executeAggregate(Repository repository, ExecuteContext executeContext, ExecutePlan executePlan, ISortingCursor cursor, boolean closeResultCursor, List<OrderBy> groupByList, List<Function> aggregateFunctionList) throws RuntimeException {
        List<Item> itemList = null;
        if (executePlan instanceof QueryWithIndex) {
            itemList = ((QueryWithIndex) executePlan).getItemList();
        } else if (executePlan instanceof Join) {
            itemList = ((Join) executePlan).getItemList();
        } else if (executePlan instanceof MergeQuery) {
            itemList = ((MergeQuery) executePlan).getItemList();
        }

        if (itemList != null) {
            if ((aggregateFunctionList != null && !aggregateFunctionList.isEmpty()) || (groupByList != null && !groupByList.isEmpty())) {
                cursor = repository.getCursorFactory().aggregateCursor(executeContext, cursor, aggregateFunctionList, groupByList, false);
            }
        }
        return cursor;
    }

    private ISortingCursor processDistinct(ExecuteContext executeContext, Query query, ISortingCursor cursor) throws RuntimeException {

        if (isDistinct(query)) {
            if (query instanceof MergeQuery && ((MergeQuery) query).isDistinctGroupByShardColumns()) {
                // do nothing
            } else {
                cursor = processOrderBy(executeContext, cursor, query, getOrderByList(query.getItemList()), false);
                cursor = new DistinctCursor(cursor, getOrderByList(query.getItemList()));
            }
        }

        return cursor;
    }

    boolean isDistinct(Query query) {
        for (Object object : query.getItemList()) {
            if (object instanceof Item && ((Item<Column>) object).isDistinct()) {
                return true;
            }
        }

        return false;
    }

    /**
     * 根据列名，生成order by 条件.永远是正向。
     */
    protected static final List<OrderBy> getOrderByList(List<Item> itemList) {
        if (itemList == null) {
            itemList = Collections.EMPTY_LIST;
        }
        List<OrderBy> orderByList = new ArrayList<OrderBy>(itemList.size());
        for (Object object : itemList) {
            Column column = Utils.getColumn(object);
            OrderBy orderBy = ObjectCreateFactory.createOrderBy();
            orderBy.setColumn(column);
            orderBy.setAsc(true);
            orderByList.add(orderBy);
        }
        return orderByList;
    }

    protected ISortingCursor processOrderBy(ExecuteContext executeContext, ISortingCursor cursor, Query Query, List<OrderBy> orderByListInRequest, boolean needOrderMatch) throws RuntimeException {
        Repository repository = executeContext.getRepository();
        boolean hasOrderBy = orderByListInRequest != null && !orderByListInRequest.isEmpty();
        if (!hasOrderBy) {
            return cursor;
        }
        OrderByStrategy orderByStrategy = chooseOrderByStrategy(cursor, orderByListInRequest, needOrderMatch);
        switch (orderByStrategy) {
            case temporaryTable:
                return repository.getCursorFactory().temporaryTableSortCursor(executeContext, cursor, orderByListInRequest, true, Query.getRequestId());
            case reverseCursor:
                return repository.getCursorFactory().reverseOrderCursor(executeContext, cursor);
            case normal:
                //相同表直接返回即可
                if (orderByTableNameIsMatch(cursor.getOrderByList(), orderByListInRequest)) {
                    return cursor;
                } else {//
                    return repository.getCursorFactory().setOrderByListCursor(executeContext, cursor, orderByListInRequest);
                }
            default:
                throw new ShouldNeverHappenException();
        }
    }

    private boolean orderByTableNameIsMatch(List<OrderBy> orderByListInCursor, List<OrderBy> orderByListInRequest) {
        for (int i = 0; i < orderByListInRequest.size(); i++) {
            OrderBy orderByInCursor = orderByListInCursor.get(i);
            OrderBy orderByInRequest = orderByListInRequest.get(i);
            if (!orderByInCursor.getItem().getTableName().equals(orderByInRequest.getItem().getTableName())) {
                return false;
            }
        }
        return true;
    }

    protected ISortingCursor processHavingFilter(final ExecuteContext executeContext, Query Query, ISortingCursor cursor) throws RuntimeException {
        Filter havingFilter = Query.getHaving();
        if (havingFilter != null) {
            cursor = executeContext.getRepository().getCursorFactory().valueFilterCursor(executeContext, cursor, Query.getHaving());
        }
        return cursor;
    }

    protected ISortingCursor processLimitFromTo(final ExecuteContext executeContext, Query Query, ISortingCursor cursor) throws RuntimeException {
        if (Query.getLimitTo() == null || (Long) Query.getLimitTo() == 0l) {// 如果limitTo为空或为0，则意味着不需要包装limit
            return cursor;
        }

        if (Query.getLimitFrom() == null) {
            log().warn("should not be here ,limit from is null .but this val should be fill with 0 before there");
            Query.setLimitFrom(0);
        }
        // limit to 不为空，limit from也不为空。所以包装个limit from To cursor 。如果limit from 也不
        cursor = executeContext.getRepository().getCursorFactory().limitFromToCursor(executeContext, cursor, (Long) Query.getLimitFrom(), (Long) Query.getLimitTo());
        return cursor;
    }

    /**
     * 用于处理列的选择性filter 如 A join B 总共有3列 A.a,A.b,B.a 实际上只需要两列。那么 这里就允许进行列的filter
     */
    protected ISortingCursor processColumnAndAlias(final ExecuteContext executeContext, Query Query, ISortingCursor cursor) throws RuntimeException {
        if (Query.getItemList() == null || Query.getItemList().isEmpty()) {
            return cursor;
        }
        Repository repository = executeContext.getRepository();
        List<Item> itemList = Query.getItemList();
        // 过滤多其它不必要的select字段
        cursor = repository.getCursorFactory().columnAliasCursor(executeContext, cursor, itemList, Query.getAlias());
        return cursor;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * <pre>
     * 算order应该用什么方法来实现的方法。 具体可以看OrderByResult的解说
     * 1.如果全部列都匹配，并且asc也全部匹配，那么认为是normal.
     * 2.只要有一个列不匹配，那么就是临时表
     * 3. 第三中情况略微复杂，做个详细说明
     *                       用户的request里面有可能会出现几种情况
     *                       1.用户请求排序顺序与实际数据顺序完全一致，那么应该正常返回。
     *                       2.用户请求顺序与实际数据顺序完全相反，那么应该返回反转cursor
     *                       3.用户请求顺序与实际数据顺序出现反转后反转，返回临时表。
     *                       （比如用户请求:order by colA(asc),B(desc),C(asc) 真正的数据顺序 A(desc),B(asc),C(desc) 。
     *                       <pre>
     * @param needOrderMatch  为true时，ordersInRequest和ordersInCursor必须顺序一致才会认定为不需要排序
     *                                              为false时，只要ordersInCursor包含ordersInRequest中的列即可
     *                                              ，不必要顺序一致，亦不考虑order的direction
     */
    protected static OrderByStrategy chooseOrderByStrategy(ISortingCursor cursor, List<OrderBy> orderByListInRequest, boolean needOrderMatch) throws RuntimeException {
        if (cursor.getJoinOrderByListList() != null && cursor.getJoinOrderByListList().size() > 1) {
            OrderByStrategy result = OrderByStrategy.temporaryTable;//最低级的
            for (List<OrderBy> orderByListInCursor : cursor.getJoinOrderByListList()) {
                if (orderByListInCursor == null) {
                    orderByListInCursor = Collections.emptyList();
                }
                //这里是最终的OrderByStrategy,其他的orderByListInCursor,也是一样的结果
                //只能是temporaryTable  reverseCursor normal 这三种的一种
                // 不可能出现一个匹配reverse，另一个匹配normal的情况
                OrderByStrategy orderByStrategy = chooseOrderByStrategy(orderByListInCursor, orderByListInRequest, needOrderMatch);
                if (orderByStrategy == OrderByStrategy.normal) {
                    return orderByStrategy;
                    //[temporaryTable  reverseCursor]多次走这值基本相同
                } else if (orderByStrategy.ordinal() > result.ordinal()) {
                    result = orderByStrategy;
                }
            }
            // 没有匹配的normal/reverseCurosr，直接返回临时表
            return result;
        } else {
            List<OrderBy> orderByListInCursor = cursor.getOrderByList();
            if (orderByListInCursor == null) {
                orderByListInCursor = Collections.emptyList();
            }
            return chooseOrderByStrategy(orderByListInCursor, orderByListInRequest, needOrderMatch);
        }
    }

    /**
     * @param needOrderMatch 为true时，ordersInRequest和ordersInCursor必须顺序一致才会认定为不需要排序
     *                       为false时，只要ordersInCursor包含ordersInRequest中的列即可
     *                       ，不必要顺序一致，亦不考虑order的direction
     */
    protected static OrderByStrategy chooseOrderByStrategy(List<OrderBy> orderByListInCursor, List<OrderBy> orderByListInRequest, boolean needOrderMatch) {
        if (!needOrderMatch) {
            return chooseOrderByStrategyNotNeedOrderMatch(orderByListInCursor, orderByListInRequest);
        } else {
            return chooseOrderByStrategyNeedOrderMatch(orderByListInCursor, orderByListInRequest);
        }
    }

    /**
     * 只要ordersInCursor中包含所有的ordersInRequest，不论方向顺序，则不需要排序 用于distinct
     */
    protected static OrderByStrategy chooseOrderByStrategyNotNeedOrderMatch(List<OrderBy> orderByListInCursor, List<OrderBy> orderByListInRequest) {
        if (!(orderByListInRequest != null && orderByListInRequest.size() <= orderByListInCursor.size())) {
            return OrderByStrategy.temporaryTable;
        } else {
            int orderByListInRequestSize = orderByListInRequest.size();
            OrderByStrategy orderByStrategy = null;
            for (int i = 0; i < orderByListInRequestSize; i++) {
                OrderBy orderByInRequest = orderByListInRequest.get(i);
                boolean columnNotMatch = true;//columnNotMatch == false判断匹配写法
                for (int j = 0; j < orderByListInCursor.size(); j++) {
                    OrderBy orderByInCursor = orderByListInCursor.get(j);
                    columnNotMatch = columnNotMatch & !isTwoOrderByColumnInfoMatched(orderByInCursor, orderByInRequest);//只比较列是否相同
                    //找到一个匹配的
                    if (columnNotMatch == false) {
                        break;
                    }
                }
                if (columnNotMatch) {//不匹配
                    return OrderByStrategy.temporaryTable;
                } else {
                    orderByStrategy = OrderByStrategy.normal;
                }
            }
            if (orderByStrategy != null) {
                return orderByStrategy;
            } else {
                return OrderByStrategy.temporaryTable;
            }
        }
    }

    private static OrderByStrategy chooseOrderByStrategyNeedOrderMatch(List<OrderBy> orderByListInCursor, List<OrderBy> orderByListInRequest) {
        //请求的字段不能超过游标里面字段,且列匹配,这个是大前提
        if (!(orderByListInRequest != null && orderByListInRequest.size() <= orderByListInCursor.size())) {
            return OrderByStrategy.temporaryTable;
        } else {
            int orderByListInRequestSize = orderByListInRequest.size();
            boolean first = true;
            boolean firstOrderByInCursor = true;
            OrderByStrategy orderByStrategy = null;
            for (int i = 0; i < orderByListInRequestSize; i++) {
                // 在当前cursor(也就是原本数据中的order by
                OrderBy orderByInCursor = orderByListInCursor.get(i);
                OrderBy orderByInRequest = orderByListInRequest.get(i);
                Boolean asc = orderByInCursor.getAsc();
                if (asc == null) {
                    return OrderByStrategy.temporaryTable;//最为保守的策略
                }
                if (first) {
                    firstOrderByInCursor = orderByInCursor.getAsc();//获取数据后才进行修改状态
                    first = false;
                }
                boolean columnNotMatch = !isTwoOrderByColumnInfoMatched(orderByInCursor, orderByInRequest);
                if (columnNotMatch) {
                    return OrderByStrategy.temporaryTable;
                }
                /**
                 * <pre>
                 * 1.当cursor中的顺序全部相同，并且request中的顺序也全部相同,cursor和request中的顺序逆序,才能使用reverse
                 * 2.当cursor中的顺序不同，但与request中的顺序相同，可以使用normal
                 * 3. 其他都是临时表
                 * </pre>
                 */
                //cursor中所有的order by列顺序一致
                if (firstOrderByInCursor == orderByInCursor.getAsc()) {
                    //下面两个判断保证是完全逆序的
                    if (orderByInCursor.getAsc() != orderByInRequest.getAsc()) {
                        if (orderByStrategy == null) {//[1]
                            orderByStrategy = OrderByStrategy.reverseCursor;
                        } else if (orderByStrategy != OrderByStrategy.reverseCursor) {//[2]
                            return OrderByStrategy.temporaryTable;
                        } else {
                            //...reverseCursor
                        }
                    } else {
                        //if (orderByInCursor.getAsc() == orderByInRequest.getAsc()) normal
                        //下面两个判断保证是完全非逆序的
                        if (orderByStrategy == null) {
                            orderByStrategy = OrderByStrategy.normal;
                        } else if (orderByStrategy != OrderByStrategy.normal) {
                            return OrderByStrategy.temporaryTable;
                        } else {
                            //...normal
                        }
                    }
                } else {
                    //cursor中的顺序不同,必须要求与request中的顺序相同,不然temporaryTable
                    if (orderByInCursor.getAsc() != orderByInRequest.getAsc()) {
                        return OrderByStrategy.temporaryTable;
                    } else {
                        //if (orderByInCursor.getAsc() == orderByInRequest.getAsc()) normal
                        if (orderByStrategy == null) {
                            orderByStrategy = OrderByStrategy.normal;//完全一致
                        } else if (orderByStrategy != OrderByStrategy.normal) {
                            return OrderByStrategy.temporaryTable;//打破完全一致的点
                        } else {
                            //...normal
                        }
                    }
                }

            }

            if (orderByStrategy != null) {
                return orderByStrategy;
            } else {
                return OrderByStrategy.temporaryTable;
            }
        }
    }

    /**
     * <pre>
     * 判断前缀索引 可判断o1是否包含o2.在组合索引里，要尽可能匹配更多的列。 在用于组合索引排序的时候 o1 是表的源组合索引。 o2
     * 是where条件中需要的走索引的key filters的组合关系。
     * 假如 ：
     * 1. 原来的组合索引(o1)是A ,B -> PK 如果o2 columnName filter是 A ,则返回0
     * 2. 如果o2 columnName filter是A,B 则返回0 ,  o2 Key Filter是C 返回-1, 无关其他属性，返回0，也就意味着不需要排序。
     *
     * -1 一般来说会导致使用临时表。 0不会
     * </pre>
     *
     * @param cursorOrderByList         索引本身的顺序[主]
     * @param joinColumnListOrderByList 条件，参数[依附]
     * @return 0 : 完全正常匹配成功,相等 1 : 前缀匹配 -1 : 不匹配
     */
    protected final int getIndexMatchType(List<OrderBy> cursorOrderByList, List<OrderBy> joinColumnListOrderByList) {
        if (cursorOrderByList == null || joinColumnListOrderByList == null) {
            return IndexMatchType.match;
        }
        //
        if (joinColumnListOrderByList.isEmpty()) {
            return IndexMatchType.match;
        }
        //joinColumnListOrderByList!=null&&!joinColumnListOrderByList.isEmpty()&&cursorOrderByList.isEmpty()
        if (cursorOrderByList.isEmpty()) {
            return IndexMatchType.not_match;
        }
        //
        if (joinColumnListOrderByList.size() > cursorOrderByList.size()) {// o2.length比o1大，返回-1，不可用
            return IndexMatchType.not_match;
        }
        for (int i = 0; i < joinColumnListOrderByList.size(); i++) {
            if (!isTwoOrderByColumnInfoMatched(cursorOrderByList.get(i), joinColumnListOrderByList.get(i))) {
                return IndexMatchType.not_match;
            }
            if (i == joinColumnListOrderByList.size() - 1) {//last joinColumnListOrderBy
                if (cursorOrderByList.size() > joinColumnListOrderByList.size()) {
                    return IndexMatchType.prefix_match;
                } else {
                    return IndexMatchType.match;
                }
            }
        }
        return IndexMatchType.prefix_match;//prefix_match
    }

    /**
     * 比较两个排序，排序可不相同
     */
    protected static boolean isTwoOrderByColumnInfoMatched(OrderBy o1, OrderBy o2) {
        Column c1 = Utils.getIColumn(o1.getItem());
        Column c2 = Utils.getIColumn(o2.getItem());
        boolean columnMatch = c1 != null && c2 != null //
                && (c1.getTableName().equals(c2.getTableName())) && //
                (c1.getColumnName().equals(c2.getColumnName()) || (c1.getAlias() != null && c1.getAlias().equals(c2.getColumnName())));

        return columnMatch;
    }

}
